package org.mika.asm;

import java.lang.reflect.Method;
import java.util.HashMap;

import net.sf.cglib.asm.Type;
import net.sf.cglib.core.ClassEmitter;
import net.sf.cglib.core.CodeEmitter;
import net.sf.cglib.core.EmitUtils;
import net.sf.cglib.core.ReflectUtils;

final class MethodUtils {
	private MethodUtils() {}
	
	static final String METHODS = "$$methods";
	static final String INVOKE_METHOD = "invoke$private$";
	
	static String getMethodSignature(Method m) {
		StringBuffer buffer = new StringBuffer(m.getReturnType().getName()).append(".").append(m.getName());
		for(Class<?> c : m.getParameterTypes()) {
			buffer.append(".").append(c.getName());
		}
		return buffer.toString();
	}
	
	static void generateStatic(ClassEmitter ce, Class<?> clazz, Class<?> iface) throws Exception {
		CodeEmitter se = ce.begin_static();
		ASMUtils.initHashMap(ce, se, METHODS);
		
		for(Method m : iface.getDeclaredMethods()) {
			se.getstatic( ce.getClassType(), METHODS, Type.getType(HashMap.class) );
			// [methods]
			
			se.push( getMethodSignature(m) );
			// [methods, signature]
			
			EmitUtils.push_object(se, clazz);
			// [methods, signature, class] 
			
			se.push( m.getName() );
			// [methods, signature, class, methodName]
			
			EmitUtils.push_array (se, m.getParameterTypes());
			// [methods, signature, class, methodName, argTypes]
			
			se.invoke_virtual(
				Type.getType(Class.class),
				ReflectUtils.getSignature(
					Class.class.getDeclaredMethod("getDeclaredMethod", new Class[]{
						String.class, Class[].class
					})
				)
			);
			// [methods, signature, method]
			
			se.dup();
			// [methods, signature, method, method]
			se.push(true);
			// [methods, signature, method, method, true]
			se.invoke(
				ReflectUtils.getMethodInfo(
					Method.class.getMethod("setAccessible", new Class[]{
						boolean.class
					})
				)
			);										
			// [methods, signature, method]
			se.invoke(					
				ReflectUtils.getMethodInfo(
					HashMap.class.getMethod("put", new Class[]{Object.class, Object.class})
				)						
			);			
		}				
		se.end_method();
	}
}
